#pragma once
namespace SOUI {

    /**
    * This is an type which can represent an value or NULL.
    * For example "SEmptyable<int> func();" means this function can return an int or nothing.
    *
    * Code example:
    *      Emptyable<int> a = 3;
    *      Emptyable<int> b(4);
    *      a = 4;
    *      assert(!a.isEmpty());
    */
    template <class T>
    class SEmptyable {
      public:
        SEmptyable(T val)
            : m_val(val), m_empty(false) {
            
        }
        
        SEmptyable()
            : m_empty(true) {
        }
        
        bool isEmpty() const {
            return m_empty;
        }
        
        bool operator==(const SEmptyable &right) const {
            if (this->isEmpty() != right.isEmpty()) {
                return false;
            }
            
            // both is empty.
            if (this->isEmpty()) {
                return true;
            }
            
            return this->m_val == right.m_val;
        }
        
        bool operator==(const T &right) const {
            if (this->isEmpty()) {
                return false;
            }
            
            return this->m_val == right;
        }
        
        
        bool operator!=(const SEmptyable &right) const {
            return !(operator==(right));
        }
        
        bool operator!=(const T &right) const {
            return !(operator==(right));
        }
        
        bool operator<(const SEmptyable &right) const {
            if (this->isEmpty() != right.isEmpty()) {
                return this->isEmpty();
            }
            
            if (this->isEmpty()) {
                return false;
            }
            
            return this->m_val < right.m_val;
        }
        
        bool operator<(const T &right) const {
            if (this->isEmpty()) {
                return true;
            }
            
            return this->m_val < right;
        }
        
        SEmptyable &operator=(const SEmptyable &right) {
            m_val = right.m_val;
            m_empty = right.m_empty;
            return *this;
        }
        
        SEmptyable &operator=(const T &val) {
            m_val = val;
            m_empty = false;
            return *this;
        }
        
        operator T() const {
            assert(m_empty == false);
            return m_val;
        }
        
        void reset() {
            m_empty = true;
        }
        
      private:
        T m_val;
        bool m_empty;
    };
}